package com.app.core.mvc.filter;

import ch.qos.logback.classic.helpers.MDCInsertingServletFilter;
import cn.hutool.core.util.RandomUtil;
import com.app.core.constant.Constants;
import com.app.core.security.ResourceOwnerContext;
import com.app.kit.SpringKit;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;

/**
 * <pre>
 * 将请求标记存入 ThreadLocal
 * 警告：多线程时需要特殊处理
 * final Map<String, String> mdc = MDC.getCopyOfContextMap(); // 复制主线程 ThreadLocal
 * new Thread(() -> {
 *     try {
 *         MDC.setContextMap(mdc); // 设置子线程 ThreadLocal
 *         // 子线程代码
 *     } finally {
 *         MDC.clear(); // 清除子线程 ThreadLocal
 *     }
 * }).start();
 * @author qiangt
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "traceFilter")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class TraceFilter extends MDCInsertingServletFilter {

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        try {
            HttpServletRequest req = (HttpServletRequest) request;
            String rid = ((HttpServletRequest) request).getHeader(Constants.TRACE_KEY);
            if (Strings.isNullOrEmpty(rid)) {
                rid = this.getRidAdapter().getRid();
            }
            MDC.put(Constants.TRACE_KEY, rid);
            MDC.put(Constants.TRACE_USER_ID_KEY, getLoginUserId(req));
            ((HttpServletResponse) response).addHeader(Constants.TRACE_KEY, TraceFilter.get());
            super.doFilter(request, response, chain);
        } finally {
            MDC.remove(Constants.TRACE_KEY);
            MDC.remove(Constants.TRACE_USER_ID_KEY);
        }
    }

    /**
     * 获取请求标记生成适配器
     *
     * @return {@link IRidAdapter}
     */
    protected IRidAdapter getRidAdapter() {
        return new DefaultAdapter();
    }

    /**
     * 获取 ThreadLocal 值
     *
     * @return {@link String}
     */
    public static String get() {
        return MDC.get(Constants.TRACE_KEY);
    }

    /**
     * 请求标记适配接口
     */
    public interface IRidAdapter {
        /**
         * 获取请求标记
         *
         * @return {@link String}
         */
        String getRid();
    }

    /**
     * 8位数字和字母随机组合
     */
    public static class DefaultAdapter implements IRidAdapter {
        @Override
        public String getRid() {
            return RandomUtil.randomString(8);
        }
    }

    /**
     * 使用 uuid
     */
    public static class UuidAdapter implements IRidAdapter {
        @Override
        public String getRid() {
            return UUID.randomUUID().toString().replace("-", "");
        }
    }

    private static String getLoginUserId(HttpServletRequest request) {
        ResourceOwnerContext context = SpringKit.getBean(ResourceOwnerContext.class);
        return context.getUserId(request);
    }

    /**
     * 使用 6 位随机数
     */
    public static class RandomNumberAdapter implements IRidAdapter {
        @Override
        public String getRid() {
            return RandomUtil.randomString(6);
        }
    }

}
